Memprediksi jenis kelamin dari nama bahasa Indonesia menggunakan Machine Learning

Loading dataset


In [2]:
import pandas as pd                 # pandas is a dataframe library
df = pd.read_csv("./data/data-pemilih-kpu.csv", encoding = 'utf-8-sig')

In [109]:
#dimensi dataset terdiri dari 13137 baris dan 2 kolom
df.shape


Out[109]:
(12950, 2)

In [6]:
#melihat 5 baris pertama dataset
df.head(5)


Out[6]:
nama jenis_kelamin
0 ERWIN TJAHJONO Laki-Laki
1 DAVIANDRIE ANDIKA BAHROENY Laki-Laki
2 ELAN KURNIAWAN Laki-Laki
3 AYU DWI CAHYANING MUKTI Perempuan
4 WAHYOEDIN Laki-Laki

In [5]:
#melihat 5 baris terakhir dataset
df.tail(5)


Out[5]:
nama jenis_kelamin
13132 HERMANSYAH Laki-Laki
13133 SITA.HJ Perempuan
13134 MASNI TAMBUNAN Perempuan
13135 MARJANEDI Laki-Laki
13136 NGALIMAN Laki-Laki

Cleansing dataset


In [7]:
# mengecek apakah ada data yang berisi null
df.isnull().values.any()


Out[7]:
True

In [8]:
# mengecek jumlah baris data yang berisi null
len(df[pd.isnull(df).any(axis=1)])


Out[8]:
187

In [9]:
# menghapus baris null dan recheck kembali
df = df.dropna(how='all')
len(df[pd.isnull(df).any(axis=1)])


Out[9]:
0

In [10]:
# mengecek dimensi dataset
df.shape


Out[10]:
(12950, 2)

In [11]:
# mengubah isi kolom jenis kelamin dari text menjadi integer (Laki-laki = 1; Perempuan= 0)
jk_map = {"Laki-Laki" : 1, "Perempuan" : 0}
df["jenis_kelamin"] = df["jenis_kelamin"].map(jk_map)

In [12]:
# cek kembali data apakah telah berubah
df.head(5)


Out[12]:
nama jenis_kelamin
0 ERWIN TJAHJONO 1
1 DAVIANDRIE ANDIKA BAHROENY 1
2 ELAN KURNIAWAN 1
3 AYU DWI CAHYANING MUKTI 0
4 WAHYOEDIN 1

In [19]:
# Mengecek distribusi jenis kelamin pada dataset

num_obs = len(df)
num_true = len(df.loc[df['jenis_kelamin'] == 1])
num_false = len(df.loc[df['jenis_kelamin'] == 0])
print("Jumlah Pria:  {0} ({1:2.2f}%)".format(num_true, (num_true/num_obs) * 100))
print("Jumlah Wanita: {0} ({1:2.2f}%)".format(num_false, (num_false/num_obs) * 100))


Jumlah Pria:  6162 (47.58%)
Jumlah Wanita: 6788 (52.42%)

Split Dataset

Dataset yang adalah akan dipecah menjadi dua bagian, 70% data akan digunakan sebagai data training untuk melatih mesin. Kemudian 30% sisanya akan digunakan sebagai data testing untuk mengevaluasi akurasi predisksi machine learning.


In [20]:
from sklearn.model_selection import train_test_split

feature_col_names = ["nama"]
predicted_class_names = ["jenis_kelamin"]

X = df[feature_col_names].values     
y = df[predicted_class_names].values
split_test_size = 0.30

text_train, text_test, y_train, y_test = train_test_split(X, y, test_size=split_test_size, stratify=y, random_state=42)

Dataset telah dipecah menjadi 2 bagian, mari kita cek distribusi nya.


In [18]:
print("Dataset Asli Pria       : {0} ({1:0.2f}%)".format(len(df.loc[df['jenis_kelamin'] == 1]), (len(df.loc[df['jenis_kelamin'] == 1])/len(df.index)) * 100.0))
print("Dataset Asli Wanita     : {0} ({1:0.2f}%)".format(len(df.loc[df['jenis_kelamin'] == 0]), (len(df.loc[df['jenis_kelamin'] == 0])/len(df.index)) * 100.0))
print("")
print("Dataset Training Pria   : {0} ({1:0.2f}%)".format(len(y_train[y_train[:] == 1]), (len(y_train[y_train[:] == 1])/len(y_train) * 100.0)))
print("Dataset Training Wanita : {0} ({1:0.2f}%)".format(len(y_train[y_train[:] == 0]), (len(y_train[y_train[:] == 0])/len(y_train) * 100.0)))
print("")
print("Dataset Test Pria       : {0} ({1:0.2f}%)".format(len(y_test[y_test[:] == 1]), (len(y_test[y_test[:] == 1])/len(y_test) * 100.0)))
print("Dataset Test Wanita     : {0} ({1:0.2f}%)".format(len(y_test[y_test[:] == 0]), (len(y_test[y_test[:] == 0])/len(y_test) * 100.0)))


Dataset Asli Pria       : 6162 (47.58%)
Dataset Asli Wanita     : 6788 (52.42%)

Dataset Training Pria   : 4313 (47.58%)
Dataset Training Wanita : 4752 (52.42%)

Dataset Test Pria       : 1849 (47.59%)
Dataset Test Wanita     : 2036 (52.41%)

Terlihat hasilnya, dataset yang telah dipecah dua tetap dapat mempertahankan persentase distribusi jenis kelamin seperti pada dataset asli.

Features Extraction

Proses features extraction, berpengaruh terhadap hasil akurasi yang didapatkan nantinya. Disini saya kan menggunakan metode simple yaitu CountVectorizer yang akan membuat matrix frekwensi kemunculan dari suatu karakter di tiap nama yang diberikan, dengan opsi analisa ngram_range 2 - 6 hanya di dalam satu kata saja. Misal Muhammad Irfani Sahnur, menghasilkan n-gram :

  • mu
  • ham
  • mad
  • nur
  • dst

In [29]:
from sklearn.feature_extraction.text import CountVectorizer

vectorizer = CountVectorizer(analyzer = 'char_wb', ngram_range=(2,6))
vectorizer.fit(text_train.ravel())

X_train = vectorizer.transform(text_train.ravel())
X_test = vectorizer.transform(text_test.ravel())

Logistic Regression

Percobaan pertama menggunakan algoritma Logistic Regression. Data hasil feature extraction akan diinput sebagai data training.


In [34]:
from sklearn.linear_model import LogisticRegression

clf = LogisticRegression()
clf.fit(X_train, y_train.ravel())


Out[34]:
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
          intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
          penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
          verbose=0, warm_start=False)

Akurasi prediksi menggunakan data test yang didapat cukup lumayan berada pada tingkat 93.6%


In [36]:
# dataset training
print(clf.score(X_train, y_train))

# dataset test
print(clf.score(X_test, y_test))


0.996690568119
0.936422136422

Detail akurasi metriks


In [43]:
from sklearn import metrics

clf_predict = clf.predict(X_test)

# training metrics
print("Accuracy: {0:.4f}".format(metrics.accuracy_score(y_test, clf_predict)))
print(metrics.confusion_matrix(y_test, clf_predict, labels=[1, 0]) )
print("")
print("Classification Report")
print(metrics.classification_report(y_test, clf_predict, labels=[1,0]))


Accuracy: 0.9364
[[1727  122]
 [ 125 1911]]

Classification Report
             precision    recall  f1-score   support

          1       0.93      0.93      0.93      1849
          0       0.94      0.94      0.94      2036

avg / total       0.94      0.94      0.94      3885

Testing prediksi jenis kelamin


In [80]:
jk_label = {1:"Laki-Laki", 0:"Perempuan"}

test_predict = vectorizer.transform(["niky felina"])
res = clf.predict(test_predict)

print(jk_label[int(res)])


Perempuan

Menggunakan Pipeline

Scikit memiliki fitur untuk memudahkan proses diatas dengan mengguanakan Pipeline. Penulisan kode jadi lebih simple dan rapih, berikut konversi kode diatas jika menggunakan Pipeline


In [76]:
from sklearn.pipeline import Pipeline

clf_lg = Pipeline([('vect', CountVectorizer(analyzer = 'char_wb', ngram_range=(2,6))),
                     ('clf', LogisticRegression()),
])
_ = clf_lg.fit(text_train.ravel(), y_train.ravel())
predicted = clf_lg.predict(text_test.ravel())
np.mean(predicted == y_test.ravel())


Out[76]:
0.93642213642213645

Tingkat akurasi persis sama, dan lebih mudah dalam penulisan kode nya. Mari kita lakukan kembali testing prediksi jenis kelamin


In [79]:
result = clf_lg.predict(["muhammad irfani sahnur"])
print(jk_label[result[0]])


Laki-Laki

Naive Bayes

Algoritma berikutnya yang akan digunakan adalah Naive Bayes. Lansung saja kita coba


In [83]:
from sklearn.naive_bayes import MultinomialNB

clf_nb = Pipeline([('vect', CountVectorizer(analyzer = 'char_wb', ngram_range=(2,6))),
                     ('clf', MultinomialNB()),
])

clf_nb = clf_nb.fit(text_train.ravel(), y_train.ravel())
predicted = clf_nb.predict(text_test.ravel())
np.mean(predicted == y_test.ravel())


Out[83]:
0.93307593307593306

Dengan algoritman Naive Bayes, tingkat akurasi yang didapatkan sedikit saja lebih rendah dari Logistic Regression yaitu 93.3%. Mari kita lakukan kembali testing prediksi jenis kelamin


In [99]:
result = clf_nb.predict(["Alifah Rahmah"])
print(jk_label[result[0]])


Perempuan

Random Forest

Algoritma terakhir yang akan digunakan adalah Random Forest. Lansung saja kita coba


In [105]:
from sklearn.ensemble import RandomForestClassifier

clf_rf = Pipeline([('vect', CountVectorizer(analyzer = 'char_wb', ngram_range=(2,6))),
                     ('clf', RandomForestClassifier(n_estimators=90, n_jobs=-1)),
])

clf_rf = clf_rf.fit(text_train.ravel(), y_train.ravel())
predicted = clf_rf.predict(text_test.ravel())
np.mean(predicted == y_test.ravel())


Out[105]:
0.93127413127413128

Dengan algoritman Random Forest, tingkat akurasi yang didapatkan lebih rendah dari dua algoritma sebelumnya, itu sebesar 93.12%. Algoritma ini juga mempunyai kekurangan, yaitu performance yang yang lebih lambat. Ok, Mari kita lakukan kembali testing prediksi jenis kelamin


In [108]:
result = clf_rf.predict(["Yuni ahmad"])
print(jk_label[result[0]])


Laki-Laki

Github Repository

Saya telah membuat implementasi aplikasi prediksi jenis kelamin, silahkan cek di github.